//
//  ScketSvr.m
//  WiFiScale
//
//  Created by Yongfeng Li on 11/20/10.
//  Copyright 2010 Nci Technology Inc. All rights reserved.
//

#import <UIKit/UIKit.h>

#include <netdb.h>
//#include <arpa/inet.h>
//#include <arpa/NSHost.h>

#import "NcitWiFiDev.h"
//#import "HRateItem.h"

#define IP_PORT 8899	//701
#define _PORTHOST 8997

char szConnOK[] = "ConnOk\r\n";//"iPhone Connected for Sending!\r\n\r\n";
char szRecvOK[] = "DataOk\r\n";//"And Send Data OK.";
char szByeBye[] = "bye!\r\n";//"\r\niPhone has just disconnected!";
NSString * const TCPServerDomainError = @"TCPServerErrorDomain";

typedef enum {
	kTCPServerCouldNotBindToIPv4Address = 1,
	kTCPServerCouldNotBindToIPv6Address = 2,
	kTCPServerNoSocketsAvailable = 3,
} TCPServerErrorCode;

//static CFReadStreamRef _readStream = nil;
//static CFWriteStreamRef _writeSream = nil;

@implementation NcitWiFiDev {
    BOOL _bWiFiConned;
    id svrDelegate;
    NSArray* m_dataSent;
    NSMutableString* _sLine;
    
    //socket
    in_addr_t m_nIpAddr;
	uint16_t m_nPort;
	
	int m_nIdx;
	CFHostRef _host;
    
    CFDataRef connectAddr;
    CFDataRef localAddr;
    
	CFSocketRef _ipv4socket;
}

@synthesize svrDelegate, m_dataSent, _sLine;
@synthesize m_nIpAddr, m_nPort;

- (void) _showAlert:(NSString *)title {
	UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:
                              NSLocalizedString(@"CheckWANConn", nil)/* @"Check your networking configuration."*/
                              delegate:svrDelegate cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil];
	[alertView show];
	//[alertView release];
}

-(id) init
{
    self = [super init];
    
    _bWiFiConned = NO;
    m_nIdx = -1;
	_host = NULL;
	_ipv4socket = NULL;
    m_dataSent = nil;
    
    //_sLine = [[NSMutableString alloc] initWithCapacity:128];
    _sLine = [[NSMutableString alloc] init];
    [_sLine setString:@""];
    
    return self;
}

-(id)init:(in_addr_t)nIpAddr port: (uint16_t)nPort {
    self = [super init];
    
    _bWiFiConned = NO;
	m_nIdx = -1;
	_host = NULL;
	_ipv4socket = NULL;
    m_dataSent = nil;
	
    m_nIpAddr = nIpAddr;
    if (nPort == 0)
        m_nPort = IP_PORT;
    else
        m_nPort = nPort;
    
    //_sLine = [[NSMutableString alloc] initWithCapacity:128];
    _sLine = [[NSMutableString alloc] init];
    [_sLine setString:@""];
	//m_dataSent = dataSent;
    
	return self;
}

-(id)setAddrPort:(in_addr_t)nIpAddr port: (uint16_t)nPort {
	/*m_nIdx = -1;
	_host = NULL;
	_ipv4socket = NULL;
    m_dataSent = nil;*/
	
    m_nIpAddr = nIpAddr;
    if (nPort == 0)
        m_nPort = IP_PORT;
    else
        m_nPort = nPort;
    
    //_sLine = [[NSMutableString alloc] initWithCapacity:128];
    //[_sLine setString:@""];
	//m_dataSent = dataSent;
    
	return self;
}

-(void) setTypeIdx:(int)nIdx {
    m_nIdx = nIdx;
}

- (void)dealloc {
    //[_sLine release];
	[self stop];
	//[super dealloc];
}

#pragma mark -
#pragma mark send data
- (BOOL)sendData:(CFSocketNativeHandle)sock Message:(char *)sMsg Alert:(BOOL) bShow {
    if(sock == 0 || _ipv4socket == NULL)
        return NO;
    if(sock == -1) {
        [self stop];
        [self startOn];
        if(_ipv4socket == NULL)
            return NO;
        sock = CFSocketGetNative(_ipv4socket);
    }
	ssize_t nSendLen = 0;
	char *szMsg = sMsg; //szSayBye;
	size_t nSendNed = strlen(szMsg);
		
	while(nSendLen < nSendNed) {
        ssize_t nSend = send(sock, szMsg+nSendLen, nSendNed-nSendLen, 0);
        if(nSend > 0)
            nSendLen += nSend;
		if(nSend < 0) {
			if(bShow) {
                [self _showAlert: NSLocalizedString(@"NoConnHost", nil)];//]@"Failed connecting"];
			}
            if(svrDelegate == nil)
                continue;
            if (svrDelegate && [svrDelegate respondsToSelector:@selector(connFailed)])
                [svrDelegate connFailed];
			break;
		}
	}
    BOOL bOK = (int)nSendLen >= (int)nSendNed;
    return bOK;  
}

- (BOOL)sendData:(char *)sMsg Alert:(BOOL) bShow {
	if (_ipv4socket != NULL) {
		CFSocketNativeHandle sock = CFSocketGetNative(_ipv4socket);
		return [self sendData:sock Message:sMsg Alert:bShow];
	}
  else if(bShow) {
    NSError *error;
    NSLog(@"Server maybe not run: %@", error);
    NSString* sMsgTips = @"WiFi device maybe not turn on";
    if (m_nPort != IP_PORT)
      sMsgTips = @"No Dev. Connnected"; //NSLocalizedString(@"NoConnHost", nil);//@"Host connections failed";
		//UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:sMsgTips message:@"Check your networking configuration." delegate:svrDelegate cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil];
		//[alertView show];
		//[alertView release];
        [self _showAlert:sMsgTips];
		//[error release];
  }
  return NO;
}

- (BOOL) sendHexData:(UInt8*) pData alert:(BOOL)bShow
{
  if (_ipv4socket != NULL) {
    CFSocketNativeHandle sock = CFSocketGetNative(_ipv4socket);
    char* szDate = (char*)pData;
    return [self sendData:sock Message:szDate Alert:bShow];
  }
  else if(bShow) {
    NSError *error;
    NSLog(@"Server maybe not run: %@", error);
    NSString* sMsgTips = @"WiFi device maybe not turn on";
    if (m_nPort != IP_PORT)
      sMsgTips = @"No Dev. Connnected"; //NSLocalizedString(@"NoConnHost", nil);//@"Host connections failed";
    [self _showAlert:sMsgTips];
  }
  return NO;
  
}


#pragma mark -
#pragma mark send data for communication
- (BOOL) sendDataOn:(char *)sMsg Alert:(BOOL) bShow {
	m_nIdx = -1;
    //[self stop];
    if (_ipv4socket == NULL) {
        NSError *error;
        [self start:m_nIpAddr ErrMsg:&error];
    }
    
	if(_ipv4socket) {
        return [self sendData:sMsg Alert:bShow];
	}
    else {        
        if (svrDelegate && [svrDelegate respondsToSelector:@selector(connFailed)])
            [svrDelegate connFailed];

		return NO;
    }
}

- (BOOL) syncDataOn:(char *)sMsg Alert:(BOOL) bShow {
    m_nIdx = -2;
    //[self stop];
    if (_ipv4socket == NULL) {
        NSError *error;
        [self start:m_nIpAddr ErrMsg:&error];
    }
    
	if(_ipv4socket) {
        [self sendData:sMsg Alert:bShow];
        return YES;
	}
    else {        
        if (svrDelegate && [svrDelegate respondsToSelector:@selector(connFailed)])
            [svrDelegate connFailed];
        
		return NO;
    }
}

- (BOOL) userSignin:(char *) sMsg Alert:(BOOL) bShow {
    m_nIdx = -9;
    if (_ipv4socket == NULL) {
        NSError *error;
        [self start:m_nIpAddr ErrMsg:&error];
    }
    
	if(_ipv4socket) {
        [self sendData:sMsg Alert:bShow];
        return YES;
	}
    else {
        if (svrDelegate && [svrDelegate respondsToSelector:@selector(connFailed)])
            [svrDelegate connFailed];
        
		return NO;
    }
}

//not call right now
- (BOOL) turnConnOn {
	if (m_dataSent == nil) {
		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"No any data" message:@"Check your data for sending." delegate:svrDelegate cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil];
		[alertView show];
		//[alertView release];
		return NO;
	}
	
	m_nIdx = 0;
	[self stop];
	NSError *error;
	if(![self start:m_nIpAddr ErrMsg:&error]) {
		NSLog(@"Server maybe not run: %@", error);
        NSString* sMsgTips = @"WiFi scale maybe not turn on";
        if (m_nPort != IP_PORT)
            sMsgTips =  NSLocalizedString(@"NoConnHost", nil);//@"Host connections failed";
		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:sMsgTips message:@"Check your networking configuration." delegate:svrDelegate cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil];
		[alertView show];
		//[alertView release];
		//[error release];
		return NO;
	}
	return YES;
}

- (void) turnConnOff {
	[self sendData:szByeBye Alert:NO];
	[self stop];
}

- (BOOL) isConnNetwork
{
    return _ipv4socket != NULL;
}

//seems not call
/*- (void) sendDataOut:(CFSocketNativeHandle)sock {
    if (m_nIdx <= -1 || m_dataSent == nil) {
        return;
    }
	int nCount = [m_dataSent count];
	if(m_nIdx >= nCount) {
		[self turnConnOff];
		//[self.actView stopAnimating];
		if (self.svrDelegate && [self.svrDelegate respondsToSelector:@selector(finishedDataSending)]) {
			[self.svrDelegate finishedDataSending];
		}		
		return;
	}
		
	HRateItem *hItem = (HRateItem *)[m_dataSent objectAtIndex:m_nIdx];
	float fTime = [hItem.RateTime floatValue];
	float fValue = [hItem.RateValue floatValue];
	NSString *sMsg = [NSString stringWithFormat:@"(%i) %.1f: %.2f\r\n", m_nIdx+1, fTime, fValue];
	[self sendData:sock Message:(char *)[sMsg UTF8String] Alert:NO];
	
	//self.prgsBar.progress = (float)(m_nIdx + 1) / nCount;
	if (self.svrDelegate && [self.svrDelegate respondsToSelector:@selector(sendingDataProgress:xPos:yPos:)]) {
		float fStep = (float)(m_nIdx + 1) / nCount;
		[self.svrDelegate sendingDataProgress:fStep xPos:fTime yPos:fValue];
	}	
	m_nIdx++;
}*/

#pragma mark -
#pragma mark socket callback
/*- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
    NSStreamStatus state = [theStream streamStatus];
    //NSString* sMsg = [;
    NSLog(@"state: %u", (NSUInteger)state);
}*/

static void ConnectDataCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
    if(info == nil)
        return;
    //SocketSvr *svr = (__bridge_transfer SocketSvr *)info;
    NcitWiFiDev *svr = (__bridge NcitWiFiDev *)info;
    if(svr == nil || svr->_sLine == nil)
        return;
	CFSocketNativeHandle sock = CFSocketGetNative(socket);
	if (kCFSocketConnectCallBack == type) {
        if(svr.m_nPort == IP_PORT && svr->m_nIdx <= -1 && svr->m_nIdx >= -8) {
            NSDateFormatter* dtFormater = [[NSDateFormatter alloc] init];
            [dtFormater setDateFormat:@"yy-MM-dd HH:mm:ss"];
            NSString* sNow = [dtFormater stringFromDate:[NSDate date]];
            sNow = [NSString stringWithFormat:@"ConnOk: %@\r\n", sNow];
            //[dtFormater release];
            
            [svr sendData: sock Message: (char *)[sNow UTF8String] Alert: NO];
        }
        else if(svr.m_nPort == IP_PORT && svr->m_nIdx >= 0) {
            [svr sendData: sock Message: szConnOK Alert: NO];
        }
        else if(svr.m_nPort == _PORTHOST && svr->m_nIdx == 0) { //pro code
            [svr sendData: sock Message: "DeviceId" Alert: NO];
        }
	}
	else if(kCFSocketWriteCallBack == type) {
        if(svr.m_nPort == _PORTHOST && svr->m_nIdx == 0) { ////pro code
            [svr sendData: sock Message: "DeviceId" Alert: NO];
        }
        //else if(svr->m_nIdx >= 0) {
            //[svr sendDataOut:sock];
        //}
	}
	else if(kCFSocketDataCallBack == type) {
		NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
		NSString *sRecv = [[NSString alloc] initWithData:(__bridge NSData *) data encoding:enc];
        if(sRecv) {
            [svr->_sLine appendString:sRecv];  
            if([sRecv hasSuffix:@"\r\n"] || [sRecv hasSuffix:@"\0"]) {
                if (svr->svrDelegate && [svr->svrDelegate respondsToSelector:@selector(recvedWiFiScaleData:)]) {
                    [svr->svrDelegate recvedWiFiScaleData:svr._sLine];
                }
                if(svr->m_nIdx == -9) {
                    if([svr->_sLine hasPrefix:@"bye!"]) {
                        [svr stop];
                        [svr->_sLine setString:@""];
                        return;
                    }
                }
                else if(svr->m_nIdx <= -1) {
                    if(svr->m_nIdx == -1)
                        [svr sendData: sock Message: szRecvOK Alert: NO];
                }
                //else if ([sRecv hasPrefix:@"OkData"]) //hasSuffix:@"OkData"])
                    //[svr sendDataOut:sock];
            
                [svr->_sLine setString:@""];
            }
            else {
                if([sRecv hasPrefix:@"193.168.1."]) {
                    if (svr->svrDelegate && [svr->svrDelegate respondsToSelector:@selector(recvedWiFiScaleData:)]) {
                        [svr->svrDelegate recvedWiFiScaleData:[sRecv mutableCopy]];
                    }
                    [svr->_sLine setString:@""];
                }
            }
        }
        else { //hex data
            CFIndex nIdx = CFDataGetLength(data);
            const UInt8* pData = CFDataGetBytePtr(data);
            int nLen = (int)nIdx;
            /*if(nLen > 257) {
             int n = 1;
             }*/
            
            if (svr->svrDelegate && [svr->svrDelegate respondsToSelector:@selector(recvedWiFiHexData:len:)]) {
                [svr->svrDelegate recvedWiFiHexData:(UInt8*)pData len:nLen];
            }
        }
		//[sRecv release];
	}
}

#pragma mark -
#pragma mark Start socket on IP address
- (BOOL) start:(in_addr_t)nIpAddr ErrMsg:(NSError **)error {
	CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL};
	_ipv4socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP,
                                 kCFSocketConnectCallBack|kCFSocketWriteCallBack|kCFSocketDataCallBack,
                                 (CFSocketCallBack)&ConnectDataCallBack, &socketCtxt);
	
	if (NULL == _ipv4socket) {
		if (error) *error = [[NSError alloc] initWithDomain:TCPServerDomainError code:kTCPServerNoSocketsAvailable userInfo:nil];
		if (_ipv4socket) CFRelease(_ipv4socket);
		_ipv4socket = NULL;
		return NO;
	}
	
	struct sockaddr_in addr4;
	memset(&addr4, 0, sizeof(addr4));	
	
	addr4.sin_len = sizeof(addr4);
	addr4.sin_family = AF_INET;
	addr4.sin_port = htons(m_nPort);
	addr4.sin_addr.s_addr = nIpAddr;
	/*CFDataRef */connectAddr = CFDataCreate(NULL, (unsigned char *)&addr4, sizeof(addr4));
	
	if (kCFSocketSuccess != CFSocketConnectToAddress(_ipv4socket, connectAddr, 3)) {	//change -1, 5 into 10;
		if (error) *error = [[NSError alloc] initWithDomain:TCPServerDomainError code:kTCPServerCouldNotBindToIPv4Address userInfo:nil];
		if (_ipv4socket) CFRelease(_ipv4socket);
		_ipv4socket = NULL;
		return NO;
	}
	
	// set up the run loop sources for the sockets
	CFRunLoopRef cfrl = CFRunLoopGetCurrent();
	CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
	CFRunLoopAddSource(cfrl, source4, kCFRunLoopCommonModes);
	CFRelease(source4);
	
	//_nIpAddr = nIpAddr;
	return YES;
}

//- (BOOL) startUdp:(in_addr_t)nIpAddr port: (uint16_t)nPort ErrMsg:(NSError **)error {
- (BOOL) startUdp:(struct sockaddr_in)addr4 ErrMsg:(NSError **)error {
    CFSocketContext socketCtxt = {0, (__bridge void *)(self), NULL, NULL, NULL};
	_ipv4socket = CFSocketCreate(kCFAllocatorDefault, PF_INET/*PF_UNSPEC*/, SOCK_DGRAM, IPPROTO_UDP,
                                 kCFSocketReadCallBack|kCFSocketDataCallBack,
                                 (CFSocketCallBack)&ConnectDataCallBack, &socketCtxt);
	
	if (NULL == _ipv4socket) {
		if (error) *error = [[NSError alloc] initWithDomain:TCPServerDomainError code:kTCPServerNoSocketsAvailable userInfo:nil];
		if (_ipv4socket) CFRelease(_ipv4socket);
		_ipv4socket = NULL;
		return NO;
	}
	

	/*struct sockaddr_in addr4, local;
	
    memset(&addr4, 0, sizeof(addr4));
	addr4.sin_len = sizeof(addr4);
	addr4.sin_family = AF_INET;
	addr4.sin_port = htons(m_nPort);
	addr4.sin_addr.s_addr = nIpAddr;*/
	/*CFDataRef */connectAddr = CFDataCreate(NULL, (unsigned char *)&addr4, sizeof(addr4));
    
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
	local.sin_len = sizeof(addr4);
	local.sin_family = AF_INET;
	local.sin_port = addr4.sin_port;//htons(m_nPort);
    //local.sin_addr.s_addr = addr4.sin_addr.s_addr;
    localAddr = CFDataCreate(NULL, (unsigned char *)&local, sizeof(local));
    CFSocketSetAddress(_ipv4socket, localAddr);
    
    /*connect(sock, (const struct sockaddr *)&addr4, sizeof(addr4));
    int flags = fcntl(sock, F_GETFL);
    fcntl(sock, F_SETFL, flags | O_NONBLOCK);
    
    _ipv4socket = CFSocketCreateWithNative(NULL, sock, kCFSocketDataCallBack,
                             (CFSocketCallBack)&ConnectDataCallBack, &socketCtxt);
    if (NULL == _ipv4socket) {
		if (error) *error = [[NSError alloc] initWithDomain:TCPServerDomainError code:kTCPServerNoSocketsAvailable userInfo:nil];
		if (_ipv4socket) CFRelease(_ipv4socket);
		_ipv4socket = NULL;
		return NO;
	}
    assert( CFSocketGetSocketFlags(_ipv4socket) & kCFSocketCloseOnInvalidate );
    sock = -1;*/

	if (kCFSocketSuccess != CFSocketConnectToAddress(_ipv4socket, connectAddr, 5)) {	//change -1 into 10;
		if (error) *error = [[NSError alloc] initWithDomain:TCPServerDomainError code:kTCPServerCouldNotBindToIPv4Address userInfo:nil];
		if (_ipv4socket) CFRelease(_ipv4socket);
		_ipv4socket = NULL;
		return NO;
	}
	
	// set up the run loop sources for the sockets
    //CFRunLoopRef cfrl = CFRunLoopGetMain();
	CFRunLoopRef cfrl = CFRunLoopGetCurrent();
	CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
	CFRunLoopAddSource(cfrl, source4, kCFRunLoopCommonModes);
	CFRelease(source4);
	
	//_nIpAddr = nIpAddr;
	return YES;
}

- (BOOL) startWiFiConn
{
    /*_bWiFiConned = NO;
    BOOL bOK = YES;
    if (_ipv4socket) {
        [self stop];
        bOK = NO;
    }
    
    if (_ipv4socket == NULL) {
        NSError *error;
        bOK = [self start:m_nIpAddr ErrMsg:&error];
        if(!bOK) {
            if (svrDelegate && [svrDelegate respondsToSelector:@selector(connFailed)])
                [svrDelegate connFailed];
        }
    }
    _bWiFiConned = bOK;
    return _bWiFiConned;*/
  return NO;
}

- (BOOL) startOn
{
    /*[self performSelectorInBackground:@selector(startUserActindicator) withObject:nil];
    
    if (!_bWiFiConned) {
        sleep(5);
    }
    return _bWiFiConned;*/
    return [self startWiFiConn];
}

- (BOOL) stop {
	if (_ipv4socket) {
		CFSocketInvalidate(_ipv4socket);
		CFRelease(_ipv4socket);
		_ipv4socket = NULL;
	}	
	return YES;
}

- (void) bkgdKeepConn
{
    if (_ipv4socket) {
        [self stop];
    }
    if (_ipv4socket == NULL) {
        NSError *error;
        [self start:m_nIpAddr ErrMsg:&error];
    }
}

@end
